home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Devices and Hardware / Velocity Engine / VelEng Wavelet / source / QTReadWriteJPEG.c < prev    next >
Encoding:
Text File  |  2000-09-28  |  25.0 KB  |  1,017 lines  |  [TEXT/CWIE]

  1. //////////
  2. //
  3. //    File:        QTReadWriteJPEG.c
  4. //
  5. //    Contains:    Sample code for compressing and decompressing JPEG images.
  6. //
  7. //    Copyright:    © 1996-1998 by Apple Computer, Inc., all rights reserved.
  8. //
  9. //    Change History (most recent first):
  10. //
  11. //       <4>         02/03/99    rtm        reworked prompt and filename handling to remove "\p" sequences
  12. //       <3>         04/27/98    rtm        revised to uncouple this file from its Macintosh framework,
  13. //                                    to make the coding style conform to other code samples, and
  14. //                                    to make it run on Windows
  15. //       <2>         02/??/97    mwm        fixed some memory leaks; expanded JPEG parsing
  16. //       <1>         04/03/96    mwm        initial coding
  17. //       
  18. //
  19. //////////
  20.  
  21. /*
  22.     Disclaimer:    IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  23.                 ("Apple") in consideration of your agreement to the following terms, and your
  24.                 use, installation, modification or redistribution of this Apple software
  25.                 constitutes acceptance of these terms.  If you do not agree with these terms,
  26.                 please do not use, install, modify or redistribute this Apple software.
  27.  
  28.                 In consideration of your agreement to abide by the following terms, and subject
  29.                 to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
  30.                 copyrights in this original Apple software (the "Apple Software"), to use,
  31.                 reproduce, modify and redistribute the Apple Software, with or without
  32.                 modifications, in source and/or binary forms; provided that if you redistribute
  33.                 the Apple Software in its entirety and without modifications, you must retain
  34.                 this notice and the following text and disclaimers in all such redistributions of
  35.                 the Apple Software.  Neither the name, trademarks, service marks or logos of
  36.                 Apple Computer, Inc. may be used to endorse or promote products derived from the
  37.                 Apple Software without specific prior written permission from Apple.  Except as
  38.                 expressly stated in this notice, no other rights or licenses, express or implied,
  39.                 are granted by Apple herein, including but not limited to any patent rights that
  40.                 may be infringed by your derivative works or by other works in which the Apple
  41.                 Software may be incorporated.
  42.  
  43.                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  44.                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  45.                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  46.                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  47.                 COMBINATION WITH YOUR PRODUCTS.
  48.  
  49.                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  50.                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  51.                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  52.                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  53.                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  54.                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  55.                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  56. */
  57.  
  58. //////////
  59. //
  60. // header files
  61. //
  62. //////////
  63.  
  64. #include "QTReadWriteJPEG.h"
  65. #include "QTUtilities.h"
  66.  
  67.  
  68. //////////
  69. //
  70. // global variables
  71. //
  72. //////////
  73.  
  74. GWorldPtr                        gGWorld = NULL;            // the GWorld we load the image data into
  75. WindowPtr                        gImageWindow = NULL;    // the window we display the image in
  76.  
  77.  
  78. //////////
  79. //
  80. // QTUtils_ConvertCToPascalString
  81. // Convert a C string into a Pascal string.
  82. //
  83. // The caller is responsible for disposing of the pointer returned by this function (by calling free).
  84. //
  85. //////////
  86.  
  87. StringPtr QTUtils_ConvertCToPascalString (char *theString)
  88. {
  89.     StringPtr    myString = (unsigned char*)(malloc(strlen(theString) + 1));
  90.     short        myIndex = 0;
  91.  
  92.     while (theString[myIndex] != '\0') {
  93.         myString[myIndex + 1] = theString[myIndex];
  94.         myIndex++;
  95.     }
  96.     
  97.     myString[0] = (unsigned char)myIndex;
  98.     
  99.     return(myString);
  100. }
  101.  
  102.  
  103.  
  104. //////////
  105. //
  106. // QTJPEG_PromptUserForJPEGFileAndDisplay
  107. // Let the user select a JPEG image file, then display it in a window.
  108. //
  109. //////////
  110.  
  111. void QTJPEG_PromptUserForJPEGFileAndDisplay (void)
  112. {
  113.     SFTypeList                    myTypeList;
  114.     StandardFileReply            myReply;
  115.     PixMapHandle                myPixMap;    
  116.     Rect                        myRect;
  117.     OSErr                        myErr = noErr;
  118.     
  119.     // have the user select a JPEG image file
  120.     myTypeList[0] = kQTFileTypeJPEG;
  121.  
  122.     StandardGetFilePreview(NULL, 1, myTypeList, &myReply);
  123.     if (!myReply.sfGood)
  124.         goto bail;
  125.     
  126.     // read the file data into an offscreen graphics world
  127.     myErr = QTJPEG_ReadJPEG(myReply.sfFile, &gGWorld);
  128.     if (myErr != noErr)
  129.         goto bail;
  130.  
  131.     myRect = gGWorld->portRect;
  132.     myPixMap = GetGWorldPixMap(gGWorld);
  133.     if (LockPixels(myPixMap)) {
  134.  
  135.         // create a window to display the image in; then draw into that window        
  136.         MacOffsetRect(&myRect, 50, 50);
  137.         gImageWindow = NewCWindow(NULL, &myRect, myReply.sfFile.name, true, movableDBoxProc, (WindowPtr)-1L, true, 0);
  138.         if (gImageWindow == NULL)
  139.             goto bail;
  140.             
  141.         MacOffsetRect(&myRect, -50, -50);
  142.         
  143.         // copy the image from the offscreen port into the window
  144.         CopyBits(    (BitMapPtr)(*myPixMap),
  145.                     (BitMapPtr)(&(gImageWindow->portBits)),
  146.                     &myRect, 
  147.                     &gImageWindow->portRect,
  148.                     srcCopy, 
  149.                     NULL);
  150.     }
  151.  
  152. bail:
  153.     UnlockPixels(myPixMap);
  154. }
  155.  
  156.  
  157.  
  158. #if 0
  159. OSErr QTJPEG_ReadJPEGPixels (FSSpec theFile, PixMapHandle *thePixels, Rect *theRect)
  160. {
  161.     ImageDescriptionHandle         myDesc;
  162.     Handle                        myData;
  163.     PixMapHandle                myPixMap;
  164.     ICMDataProcRecord             myLoadProc;
  165.     Rect                        myRect;
  166.     DLDataRec                    myDataRec;
  167.     long                        mySize;
  168.     short                        myRefNum;
  169.     Ptr                            myDataPtr;
  170.     OSErr                        myErr = paramErr;
  171.     
  172.     // save the current graphics port
  173.     GetGWorld(&oldWorld, &oldGD);
  174.     
  175.     if (theGWorld != NULL) {
  176.             
  177.         myErr = FSpOpenDF(&theFile, fsRdWrShPerm, &myRefNum);
  178.         if (myErr == noErr) {
  179.         
  180.             myDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
  181.             if (myDesc != NULL) {
  182.                 HLock((Handle)myDesc);
  183.                 myErr = QTJPEG_ReadJPEGHeader(myRefNum, myDesc, &myRect);
  184.                 if (myErr == noErr) {
  185.                 
  186.                     myData = NewHandleClear(kBufferSize);
  187.                     myErr = MemError();
  188.                     
  189.                     if ((myData != NULL) && (myErr == noErr)) {
  190.                            *thePixels = NewHandle(4* (myRect.bottom-myRect.top) * (myRect.right-myRect.left));
  191.                         myErr = MemErr();
  192.                         if ((*thePixels != NULL) && (myErr == noErr)){
  193.                             myErr = SetFPos(myRefNum, fsFromStart, 0);
  194.                             if (myErr == noErr) {
  195.                                                             
  196.                                 mySize = kBufferSize;
  197.                                 
  198.                                 // make sure the file size is greater than the buffer size
  199.                                 (void)GetEOF(myRefNum, &mySize);
  200.                                 if (kBufferSize > mySize)
  201.                                     mySize = mySize;
  202.                                     
  203.                                 myErr = FSRead(myRefNum, &mySize, *myData);
  204.                                 if (myErr == noErr) {
  205.                                     mySize = (*myDesc)->dataSize - kBufferSize;
  206.                                     if (mySize < 0) 
  207.                                         mySize = 0;
  208.                                         
  209.                                     myDataRec.fRefNum = myRefNum;
  210.                                     myDataRec.fFileLength = mySize;
  211.                                     myDataRec.fOrigPtr = *myData;
  212.                                     myDataRec.fEndPtr = *myData + kBufferSize;
  213.                                     myLoadProc.dataProc = NewICMDataProc(QTJPEG_DataLoadingProc);
  214.                                     myLoadProc.dataRefCon = (long)&myDataRec;
  215.                                     
  216.                                     myDataPtr = StripAddress(*myData);
  217.                                     
  218.                                     myErr = FDecompressImage(    myDataPtr,
  219.                                                                 myDesc,
  220.                                                                 myPixMap,
  221.                                                                 &myRect,
  222.                                                                 NULL,
  223.                                                                 srcCopy,
  224.                                                                 NULL,
  225.                                                                 NULL,
  226.                                                                 NULL,
  227.                                                                   codecHighQuality,
  228.                                                                   anyCodec,
  229.                                                                   kBufferSize,
  230.                                                                   &myLoadProc,
  231.                                                                   NULL);
  232.                                                           
  233.                                     DisposeRoutineDescriptor(myLoadProc.dataProc);
  234.                                      HUnlock(myData);
  235.                                     UnlockPixels(myPixMap);
  236.                                     
  237.                                     // restore the previous graphics world 
  238.                                     SetGWorld(oldWorld, oldGD);
  239.                                     
  240.                                     if (myErr == noErr)
  241.                                         *theGWorld = myGWorld;
  242.                                 }
  243.                             }
  244.                             
  245.                             if (myErr != noErr)
  246.                                 DisposeGWorld(myGWorld);
  247.                         }
  248.                         
  249.                         DisposeHandle(myData);
  250.                     }
  251.                 }
  252.                 
  253.                 HUnlock((Handle)myDesc);                
  254.                 DisposeHandle((Handle)myDesc);
  255.             }
  256.             
  257.             FSClose(myRefNum);                // close the file
  258.         }
  259.     }
  260.             
  261.     return(myErr);
  262. }
  263. #endif
  264.  
  265.  
  266.  
  267.  
  268.  
  269.  
  270.  
  271. //////////
  272. //
  273. // QTJPEG_ReadJPEG
  274. // Open a JPEG file with supplied FSSpec; the graphics world containing the image is returned through theGWorld.
  275. //
  276. //////////
  277.  
  278. OSErr QTJPEG_ReadJPEG (FSSpec theFile, CGrafPtr *theGWorld)
  279. {
  280.     ImageDescriptionHandle         myDesc;
  281.     Handle                        myData;
  282.     GWorldPtr                    myGWorld = NULL;
  283.     GWorldPtr                    oldWorld;
  284.     GDHandle                    oldGD;
  285.     PixMapHandle                myPixMap;
  286.     ICMDataProcRecord             myLoadProc;
  287.     Rect                        myRect;
  288.     DLDataRec                    myDataRec;
  289.     long                        mySize;
  290.     short                        myRefNum;
  291.     Ptr                            myDataPtr;
  292.     OSErr                        myErr = paramErr;
  293.     
  294.     // save the current graphics port
  295.     GetGWorld(&oldWorld, &oldGD);
  296.     
  297.     if (theGWorld != NULL) {
  298.             
  299.         myErr = FSpOpenDF(&theFile, fsRdWrShPerm, &myRefNum);
  300.         if (myErr == noErr) {
  301.         
  302.             myDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
  303.             if (myDesc != NULL) {
  304.                 HLock((Handle)myDesc);
  305.                 myErr = QTJPEG_ReadJPEGHeader(myRefNum, myDesc, &myRect);
  306.                 if (myErr == noErr) {
  307.                 
  308.                     myData = NewHandleClear(kBufferSize);
  309.                     myErr = MemError();
  310.                     
  311.                     if ((myData != NULL) && (myErr == noErr)) {
  312.                         myErr = QTJPEG_NewJPEGWorld(&myGWorld, (*myDesc)->depth, myRect);
  313.                         if ((myGWorld != NULL) && (myErr == noErr)){
  314.                             myErr = SetFPos(myRefNum, fsFromStart, 0);
  315.                             if (myErr == noErr) {
  316.                             
  317.                                 myPixMap = GetGWorldPixMap(myGWorld);
  318.                                 LockPixels(myPixMap);
  319.                                 SetGWorld(myGWorld, NULL);
  320.                                 HLock(myData);
  321.                                 
  322.                                 mySize = kBufferSize;
  323.                                 
  324.                                 // make sure the file size is greater than the buffer size
  325.                                 (void)GetEOF(myRefNum, &mySize);
  326.                                 if (kBufferSize < mySize)
  327.                                     mySize = kBufferSize;
  328.                                     
  329.                                 myErr = FSRead(myRefNum, &mySize, *myData);
  330.                                 if (myErr == noErr) {
  331.                                     mySize = (*myDesc)->dataSize - kBufferSize;
  332.                                     if (mySize < 0) 
  333.                                         mySize = 0;
  334.                                         
  335.                                     myDataRec.fRefNum = myRefNum;
  336.                                     myDataRec.fFileLength = mySize;
  337.                                     myDataRec.fOrigPtr = *myData;
  338.                                     myDataRec.fEndPtr = *myData + kBufferSize;
  339.                                     myLoadProc.dataProc = NewICMDataProc(QTJPEG_DataLoadingProc);
  340.                                     myLoadProc.dataRefCon = (long)&myDataRec;
  341.                                     
  342.                                     myDataPtr = StripAddress(*myData);
  343.                                     
  344.                                     myErr = FDecompressImage(    myDataPtr,
  345.                                                                 myDesc,
  346.                                                                 myPixMap,
  347.                                                                 &myRect,
  348.                                                                 NULL,
  349.                                                                 srcCopy,
  350.                                                                 NULL,
  351.                                                                 NULL,
  352.                                                                 NULL,
  353.                                                                   codecHighQuality,
  354.                                                                   anyCodec,
  355.                                                                   kBufferSize,
  356.                                                                   &myLoadProc,
  357.                                                                   NULL);
  358.                                                           
  359.                                     DisposeRoutineDescriptor(myLoadProc.dataProc);
  360.                                      HUnlock(myData);
  361.                                     UnlockPixels(myPixMap);
  362.                                     
  363.                                     // restore the previous graphics world 
  364.                                     SetGWorld(oldWorld, oldGD);
  365.                                     
  366.                                     if (myErr == noErr)
  367.                                         *theGWorld = myGWorld;
  368.                                 }
  369.                             }
  370.                             
  371.                             if (myErr != noErr)
  372.                                 DisposeGWorld(myGWorld);
  373.                         }
  374.                         
  375.                         DisposeHandle(myData);
  376.                     }
  377.                 }
  378.                 
  379.                 HUnlock((Handle)myDesc);                
  380.                 DisposeHandle((Handle)myDesc);
  381.             }
  382.             
  383.             FSClose(myRefNum);                // close the file
  384.         }
  385.     }
  386.             
  387.     return(myErr);
  388. }
  389.  
  390.  
  391. //////////
  392. //
  393. // QTJPEG_ReadJPEGHeader
  394. // Read the JPEG header and fill out the specified ImageDescription with needed info.
  395. //
  396. //////////
  397.  
  398. OSErr QTJPEG_ReadJPEGHeader (short theRefNum, ImageDescriptionHandle theDesc, Rect *theRect)
  399. {
  400.     long                     mySize;
  401.     short                    mySkip;
  402.     UInt8                     myMarker;
  403.     Boolean                    isJFIF = false;
  404.     Boolean                    readingExtension = false;
  405.     OSErr                    myErr = noErr;
  406.     
  407.     // set file position to beginning of file
  408.     myErr = SetFPos(theRefNum, fsFromStart , 0);
  409.     if (myErr != noErr)
  410.         return(myErr);
  411.         
  412.     // get file length, so we don't overflow
  413.     myErr = GetEOF(theRefNum, &mySize);
  414.     if (myErr != noErr)
  415.         return(myErr);
  416.     
  417.     (*theDesc)->dataSize = mySize;
  418.  
  419.     // loop forever
  420.     while (true) {
  421.         myMarker = QTJPEG_FindNextMarker(theRefNum);
  422.         
  423.         switch (myMarker) {
  424.             case kSOIMarker:
  425.                 isJFIF = true;
  426.                 break;
  427.                 
  428.             case kAPPOMarker + 0:
  429.             case kAPPOMarker + 1:
  430.             case kAPPOMarker + 2:
  431.             case kAPPOMarker + 3:
  432.             case kAPPOMarker + 4:
  433.             case kAPPOMarker + 5:
  434.             case kAPPOMarker + 6:
  435.             case kAPPOMarker + 7:
  436.             case kAPPOMarker + 8:
  437.             case kAPPOMarker + 9:
  438.             case kAPPOMarker + 10:
  439.             case kAPPOMarker + 11:
  440.             case kAPPOMarker + 12:
  441.             case kAPPOMarker + 13:
  442.             case kAPPOMarker + 14:
  443.             case kAPPOMarker + 15:
  444.                 myErr = QTJPEG_HandleAPPOMarker(myMarker, theRefNum, theDesc, &readingExtension);
  445.                 if (myErr != noErr)
  446.                     return(myErr);
  447.                 break;
  448.                 
  449.             case kCommentMarker:
  450.                 QTJPEG_SkipLength(theRefNum);
  451.                 break;
  452.         
  453.             case kSOFMarker + 0:        // start of frame header marker
  454.             case kSOFMarker + 1:
  455.             case kSOFMarker + 2:
  456.             case kSOFMarker + 3:
  457.             case kSOFMarker + 5:
  458.             case kSOFMarker + 6:
  459.             case kSOFMarker + 7:
  460.             case kSOFMarker + 9:
  461.             case kSOFMarker + 10:
  462.             case kSOFMarker + 11:
  463.             case kSOFMarker + 13:
  464.             case kSOFMarker + 14:
  465.             case kSOFMarker + 15:
  466.                 myErr = QTJPEG_HandleSOFMarker(theRefNum, theDesc, readingExtension);
  467.                 if (myErr != noErr)
  468.                     return(myErr);
  469.                     
  470.                 if (!readingExtension) {
  471.                     MacSetRect(theRect, 0, 0, (*theDesc)->width, (*theDesc)->height);
  472.                     return(noErr);
  473.                 }
  474.                 break;
  475.                 
  476.             case kDACMarker:
  477.                 mySkip = QTJPEG_ReadWord(theRefNum) - 2;
  478.                 mySkip *= QTJPEG_ReadWord(theRefNum);
  479.                 myErr = SetFPos(theRefNum, fsFromMark, mySkip);
  480.                 break;
  481.                 
  482.             case kSOSMarker:
  483.                 QTJPEG_HandleSOSMarker(theRefNum);
  484.                 break;
  485.                 
  486.             case kDHTMarker:
  487.             case kDQTMarker:
  488.             case kRSTOMarker:
  489.                 QTJPEG_SkipLength(theRefNum);
  490.                 break;
  491.                 
  492.             case kEOIMarker:        // we reached the end of image
  493.                 // we are reading an extension so keep going
  494.                 if (readingExtension)
  495.                     readingExtension = false;
  496.                 break;
  497.         }
  498.     }
  499.     
  500.     return(myErr);
  501. }
  502.  
  503.  
  504. //////////
  505. //
  506. // QTJPEG_FindNextMarker
  507. // Find the next marker in the specified file.
  508. //
  509. //////////
  510.  
  511. UInt8 QTJPEG_FindNextMarker (long theRefNum)
  512. {
  513.     UInt8             myMarker;
  514.     
  515.     myMarker = QTJPEG_ReadByte(theRefNum);
  516.     
  517.     while (myMarker == kStartMarker)
  518.         myMarker = QTJPEG_ReadByte(theRefNum);
  519.  
  520.     while (myMarker == 0x00) {
  521.         myMarker = QTJPEG_ReadByte(theRefNum);
  522.  
  523.         while (myMarker != kStartMarker)
  524.             myMarker = QTJPEG_ReadByte(theRefNum);
  525.             
  526.         myMarker = QTJPEG_ReadByte(theRefNum);
  527.     }
  528.         
  529.     return(myMarker);
  530. }
  531.  
  532.  
  533. //////////
  534. //
  535. // QTJPEG_HandleAPPOMarker
  536. // Handle an APPO marker in the specified file.
  537. //
  538. //////////
  539.  
  540. OSErr QTJPEG_HandleAPPOMarker (UInt8 theMarker, long theRefNum, ImageDescriptionHandle theDesc, Boolean *readingExtension)
  541. {
  542.     Fixed            xRes, yRes;
  543.     long             myLength;
  544.     short            myUnits;
  545.     short            myVersion;
  546.     UInt8            myExtension;
  547.     UInt8             myType[5];
  548.     OSErr            myErr = noErr;
  549.  
  550.     // read skip bytes - header length - skip count
  551.     myLength = QTJPEG_ReadWord(theRefNum) - 2;
  552.     
  553.     if ((theMarker == kAPPOMarker) && (myLength >= 14)) {
  554.         myType[0] = QTJPEG_ReadByte(theRefNum);
  555.         myType[1] = QTJPEG_ReadByte(theRefNum);
  556.         myType[2] = QTJPEG_ReadByte(theRefNum);
  557.         myType[3] = QTJPEG_ReadByte(theRefNum);
  558.         myType[4] = QTJPEG_ReadByte(theRefNum);
  559.         
  560.         // check to see if we really have the JFIF header
  561.         if ((myType[0] == 'J') &&
  562.             (myType[1] == 'F') &&
  563.             (myType[2] == 'I') &&
  564.             (myType[3] == 'F')) {
  565.             
  566.               myVersion = QTJPEG_ReadWord(theRefNum);
  567.  
  568.             if (myVersion < 0x100)    
  569.                 return(paramErr);     // don't know this
  570.             else
  571.                 (*theDesc)->version = myVersion;
  572.                 
  573.             myUnits = QTJPEG_ReadByte(theRefNum);
  574.             xRes = QTJPEG_ReadWord(theRefNum);
  575.               yRes = QTJPEG_ReadWord(theRefNum);
  576.  
  577.             switch (myUnits) {
  578.                 case 0:            // no res, just aspect ratio
  579.                     xRes = FixMul(72L << 16, xRes << 16);
  580.                     yRes = FixMul(72L << 16, yRes << 16);
  581.                     break;
  582.                     
  583.                 case 1:            // dots per inch
  584.                     xRes = xRes << 16;
  585.                     yRes = yRes << 16;
  586.                     break;
  587.                     
  588.                 case 2:            // dots per centimeter (we convert to dpi )
  589.                     xRes = FixMul(0x28a3d, xRes << 16);
  590.                     yRes = FixMul(0x28a3d, xRes << 16);        // yRes?? RTM
  591.                     break;    
  592.                     
  593.                 default:
  594.                     break;
  595.             }
  596.             
  597.             (*theDesc)->hRes = xRes;
  598.             (*theDesc)->vRes = yRes;
  599.  
  600.             myLength -= 12;
  601.             myErr = SetFPos(theRefNum, fsFromMark, myLength);
  602.             
  603.         } else {
  604.             if ((myType[0] == 'J') &&
  605.                 (myType[1] == 'F') &&
  606.                 (myType[2] == 'X') &&
  607.                 (myType[3] == 'X')) { 
  608.                 
  609.                 *readingExtension = true;        // next markers are extensions; ignore them
  610.  
  611.                 myExtension = QTJPEG_ReadByte(theRefNum);
  612.                 switch (myExtension) {
  613.                     case 0x10:
  614.                     case 0x11:
  615.                     case 0x13:
  616.                         break;
  617.                     default:
  618.                         return(paramErr);
  619.                 }
  620.             }
  621.         }
  622.     } else
  623.         myErr = SetFPos(theRefNum, fsFromMark, myLength);
  624.  
  625.     return(myErr);
  626. }
  627.  
  628.  
  629. //////////
  630. //
  631. // QTJPEG_HandleSOFMarker
  632. // Handle an SOF marker in the specified file.
  633. //
  634. //////////
  635.  
  636. OSErr QTJPEG_HandleSOFMarker (long theRefNum, ImageDescriptionHandle theDesc, Boolean readingExtension)
  637. {
  638.     short             myWidth = 0;
  639.     short             myHeight = 0;
  640.     short             myComponents;
  641.     short            myLength;
  642.     StringPtr        myTitle = QTUtils_ConvertCToPascalString(kWindowTitle);
  643.     OSErr            myErr = noErr;
  644.  
  645.     if (!readingExtension) {
  646.         myLength = QTJPEG_ReadWord(theRefNum);
  647.         QTJPEG_ReadByte(theRefNum);
  648.         myHeight = QTJPEG_ReadWord(theRefNum);
  649.         myWidth = QTJPEG_ReadWord(theRefNum);
  650.  
  651.         // make sure we do have something to display
  652.         if ((myWidth != 0) && (myHeight != 0)) {
  653.         
  654.             // now set up the image description
  655.             (*theDesc)->idSize             = sizeof(ImageDescription);
  656.             (*theDesc)->cType             = FOUR_CHAR_CODE('jpeg');
  657.             (*theDesc)->dataRefIndex     = 0;
  658.             (*theDesc)->revisionLevel     = 0;
  659.             (*theDesc)->vendor             = 0;
  660.             (*theDesc)->temporalQuality = 0;
  661.             (*theDesc)->spatialQuality    = codecNormalQuality;
  662.             (*theDesc)->width             = myWidth;
  663.             (*theDesc)->height             = myHeight;
  664.             (*theDesc)->frameCount         = 1;
  665.             BlockMove(myTitle, (*theDesc)->name, 13);
  666.             (*theDesc)->clutID             = -1;
  667.             
  668.             myComponents = QTJPEG_ReadByte(theRefNum);
  669.             
  670.             switch (myComponents) {
  671.                 case 1:        
  672.                     (*theDesc)->depth = 40;
  673.                     break;
  674.  
  675.                 case 3:
  676.                     (*theDesc)->depth = 32;
  677.                     break;
  678.  
  679.                 case 4:
  680.                     (*theDesc)->depth = 32;
  681.                     break;
  682.                                         
  683.                 default:
  684.                     myErr = paramErr;
  685.                     return(myErr);
  686.                     break;
  687.             }
  688.             
  689.             myErr = SetFPos(theRefNum, fsFromMark, myLength - 8);
  690.             return(noErr);
  691.         }
  692.         
  693.     } else {
  694.         myLength = QTJPEG_ReadWord(theRefNum) - 2;
  695.         myErr = SetFPos(theRefNum, fsFromMark, myLength);
  696.         if (myErr != noErr)
  697.             return(myErr);
  698.     }
  699.     
  700.     free(myTitle);
  701.  
  702.     return(myErr);
  703. }
  704.  
  705.  
  706. //////////
  707. //
  708. // QTJPEG_HandleSOSMarker
  709. // Handle an SOS marker in the specified file.
  710. //
  711. //////////
  712.  
  713. void QTJPEG_HandleSOSMarker (long theRefNum)
  714. {
  715.     short        myComponents;
  716.     short        myWord;
  717.     
  718.     QTJPEG_ReadWord(theRefNum);
  719.     myComponents = QTJPEG_ReadByte(theRefNum);
  720.     
  721.     for (myWord = 0; myWord < myComponents; myWord++)
  722.         QTJPEG_ReadWord(theRefNum);
  723. }
  724.  
  725.  
  726. //////////
  727. //
  728. // QTJPEG_ReadByte
  729. // Read the next byte from the specified file.
  730. //
  731. //////////
  732.  
  733. UInt8 QTJPEG_ReadByte (short theRefNum)
  734. {
  735.     UInt8        myData;
  736.     long        myBytesNeeded = sizeof(char);
  737.  
  738.     (void)FSRead(theRefNum, &myBytesNeeded, &myData);
  739.     return(myData);
  740. }
  741.  
  742.  
  743. //////////
  744. //
  745. // QTJPEG_ReadWord
  746. // Read the next word from the specified file.
  747. //
  748. //////////
  749.  
  750. UInt16 QTJPEG_ReadWord (short theRefNum)
  751. {
  752.     UInt16        myData;
  753.     long        myBytesNeeded = sizeof(UInt16);
  754.  
  755.     (void)FSRead(theRefNum, &myBytesNeeded, &myData);
  756.     
  757.     // in JPEG files, the data is stored in a big-endian order
  758.     myData = EndianU16_BtoN(myData);
  759.     return(myData);
  760. }
  761.  
  762.  
  763. //////////
  764. //
  765. // QTJPEG_SkipLength
  766. // Skip over the length word.
  767. //
  768. //////////
  769.  
  770. void QTJPEG_SkipLength (long theRefNum)
  771. {
  772.     UInt16        mySkip;
  773.     
  774.     mySkip = QTJPEG_ReadWord(theRefNum) - 2;
  775.     SetFPos(theRefNum, fsFromMark, mySkip);
  776. }
  777.  
  778.  
  779. //////////
  780. //
  781. // QTJPEG_NewJPEGWorld
  782. // Return, through the theWorld parameter, a new offscreen graphics world suitable
  783. // for drawing a JPEG image of the specified bitdepth into.
  784. //
  785. //////////
  786.  
  787. OSErr QTJPEG_NewJPEGWorld (GWorldPtr *theWorld, short theDepth, Rect theRect)
  788. {
  789.     GWorldPtr        oldPort;
  790.     GDHandle        oldGD;
  791.     PixMapHandle    myPixMap;
  792.     CTabHandle        myCTab = NULL;
  793.     OSErr            myErr = paramErr;
  794.     
  795.     if (theWorld != NULL) {
  796.         // save the current graphics port
  797.         GetGWorld(&oldPort, &oldGD);
  798.     
  799.         // if depth is greater than 32, then the image is grayscale
  800.         if (theDepth > 32) {
  801.             myCTab = GetCTable(theDepth);
  802.             theDepth = theDepth - 32;
  803.         }
  804.         
  805.         // first try to allocate a GWorld in the application's heap
  806.         myErr = NewGWorld(theWorld, theDepth, &theRect, myCTab, NULL, 0L);
  807.         
  808.         // otherwise, try to allocate a GWorld in temporary memory
  809.         if (myErr != noErr)
  810.             myErr = NewGWorld(theWorld, theDepth, &theRect, myCTab, NULL, useTempMem);
  811.     
  812.         if ((myErr == noErr) && (theWorld != NULL))  {
  813.             myPixMap = GetGWorldPixMap(*theWorld);
  814.             if (LockPixels(myPixMap)) {
  815.                 SetGWorld(*theWorld, NULL);
  816.                 EraseRect(&theRect);
  817.                 UnlockPixels(myPixMap);
  818.             }
  819.         }
  820.         
  821.         SetGWorld(oldPort, oldGD);    
  822.     }
  823.     
  824.     return(myErr);
  825. }
  826.  
  827.  
  828. //////////
  829. //
  830. // QTJPEG_SaveJPEG
  831. // Save the specified image as a compressed file.
  832. //
  833. //////////
  834.  
  835. OSErr QTJPEG_SaveJPEG (GWorldPtr theWorld)
  836. {
  837.     StandardFileReply            myReply;
  838.     ImageDescriptionHandle         myDesc;
  839.     Handle                        myData;
  840.     Rect                        myRect;
  841.     PixMapHandle                myPixMap;    
  842.     CTabHandle                    myCTab = NULL;
  843.     ICMFlushProcRecord            myFlushProc;    
  844.     short                        myRefNum;
  845.     short                        myDepth;
  846.     StringPtr                     myImagePrompt = QTUtils_ConvertCToPascalString(kSaveImagePrompt);
  847.     StringPtr                     myImageFileName = QTUtils_ConvertCToPascalString(kSaveImageFileName);
  848.     OSErr                        myErr = paramErr;
  849.     
  850.     if (theWorld == NULL)
  851.         goto bail;
  852.     
  853.     // have the user select the name of the new image file
  854.     StandardPutFile(myImagePrompt, myImageFileName, &myReply);
  855.     if (!myReply.sfGood)
  856.         goto bail;
  857.     
  858.     myDesc = (ImageDescriptionHandle)NewHandle(sizeof(ImageDescription));
  859.     if (myDesc == NULL)
  860.         goto bail;
  861.     
  862.     myRect = theWorld->portRect;
  863.     myPixMap = GetGWorldPixMap(theWorld);
  864.     
  865.     if (LockPixels(myPixMap)) {    
  866.     
  867.         // if less than 16-bit then get the color table of our GWorld
  868.         myDepth = (**myPixMap).pixelSize;
  869.         if (myDepth < 16)
  870.             myCTab = (**myPixMap).pmTable;
  871.         
  872.         myData = NewHandle(kBufferSize);
  873.         myErr = MemError();
  874.         
  875.         if ((myData != NULL) && (myErr == noErr)) {
  876.             CodecType            myCodec = kJPEGCodecType;
  877.  
  878.             HLock(myData);
  879.                                                          
  880.             if (myReply.sfReplacing) 
  881.                 myErr = FSpDelete(&myReply.sfFile);
  882.         
  883.             myErr = FSpCreate(&myReply.sfFile, kImageFileCreator, kQTFileTypeJPEG, myReply.sfScript);
  884.             
  885.             if (myErr == noErr)
  886.                 myErr = FSpOpenDF(&myReply.sfFile, fsRdWrPerm, &myRefNum);
  887.                 
  888.             if (myErr == noErr)
  889.                 myErr = SetFPos(myRefNum, fsFromStart, 0);
  890.                 
  891.             if (myErr == noErr) {
  892.                 ICMFlushProcRecordPtr        myFlushProcPtr = NULL;
  893.  
  894.                 myFlushProc.flushProc = NewICMFlushProc(QTJPEG_DataUnloadProc);
  895.                 myFlushProc.flushRefCon = myRefNum;
  896.                 myFlushProcPtr = &myFlushProc;
  897.  
  898.                 // compress the image
  899.                 myErr = FCompressImage(    myPixMap,
  900.                                         &myRect,
  901.                                         myDepth,
  902.                                          codecNormalQuality,
  903.                                          myCodec,
  904.                                         anyCodec,
  905.                                         myCTab,
  906.                                         codecFlagWasCompressed,
  907.                                         kBufferSize,
  908.                                         myFlushProcPtr,
  909.                                         NULL,
  910.                                         myDesc,
  911.                                         *myData);
  912.                 
  913.                 if (myErr == noErr)
  914.                     myErr = SetFPos(myRefNum, fsFromStart, (**myDesc).dataSize);
  915.  
  916.                 if (myErr == noErr)
  917.                     myErr = SetEOF(myRefNum, (**myDesc).dataSize);
  918.                              
  919.                 if (myErr == noErr)        
  920.                     myErr = FSClose(myRefNum);
  921.                 
  922.                 HUnlock(myData);
  923.                 DisposeHandle(myData);
  924.                 
  925.                 DisposeRoutineDescriptor(myFlushProc.flushProc);
  926.             }
  927.         }
  928.     }
  929.     
  930.     UnlockPixels(myPixMap);
  931.     
  932. bail:
  933.     if (myDesc != NULL)
  934.         DisposeHandle((Handle)myDesc);
  935.     
  936.     free(myImagePrompt);
  937.     free(myImageFileName);
  938.  
  939.     return(myErr);
  940. }            
  941.     
  942.     
  943. //////////
  944. //
  945. // QTJPEG_DataUnloadProc
  946. // A data unloading procedure: write the compressed data to disk.
  947. //
  948. // The theRefCon parameter is assumed to be a file reference number of an open file.
  949. //
  950. //////////
  951.  
  952. PASCAL_RTN OSErr QTJPEG_DataUnloadProc (Ptr theData, long theBytesNeeded, long theRefCon)
  953. {
  954.     OSErr        myErr = noErr;
  955.     
  956.     if (theData == NULL) {
  957.         // if data is NULL, set a new position in the file from the current mark, offset by bytesNeeded
  958.         myErr = SetFPos(theRefCon, fsFromMark, theBytesNeeded);
  959.     } else {
  960.         // otherwise, write the specified data to disk
  961.         myErr = FSWrite(theRefCon, &theBytesNeeded, theData);
  962.     }
  963.     
  964.     return(myErr);
  965. }
  966.  
  967.  
  968. //////////
  969. //
  970. // QTJPEG_DataLoadingProc
  971. // A data loading procedure: read the data from disk.
  972. //
  973. // The theRefCon parameter is assumed to be a pointer to our custom data-loading record.
  974. //
  975. //////////
  976.  
  977. PASCAL_RTN OSErr QTJPEG_DataLoadingProc (Ptr *theData, long theBytesNeeded, long theRefCon)
  978. {
  979.     long        myUnusedDataLen;
  980.     long        myNewDataLen;
  981.     DLDataPtr    myDataRec;
  982.     Ptr            myDataPtr;
  983.     OSErr        myErr = noErr;
  984.     
  985.     myDataRec = (DLDataPtr)theRefCon;
  986.     
  987.     if (theData == NULL) {
  988.         myErr = SetFPos(myDataRec->fRefNum, fsFromMark, theBytesNeeded);
  989.     } else {    
  990.         myDataPtr = *theData;
  991.         
  992.         // if QT requests more data than is in the buffer, we will have to load more
  993.         if ((myDataPtr + theBytesNeeded) >= myDataRec->fEndPtr) {
  994.             // move whats left up to the front of the buffer
  995.             myUnusedDataLen = myDataRec->fEndPtr - myDataPtr;
  996.             BlockMove(myDataPtr, myDataRec->fOrigPtr, myUnusedDataLen);
  997.             
  998.             // now fill the buffer with new data,
  999.             // following the data we moved to the front of the buffer
  1000.             myNewDataLen = kBufferSize - myUnusedDataLen;
  1001.             
  1002.             if (myNewDataLen > myDataRec->fFileLength)
  1003.                 myNewDataLen = myDataRec->fFileLength;
  1004.                 
  1005.             myDataPtr = myDataRec->fOrigPtr + myUnusedDataLen;
  1006.             
  1007.             myErr = FSRead(myDataRec->fRefNum, &myNewDataLen, myDataPtr);
  1008.             
  1009.             myDataRec->fFileLength -= myNewDataLen;
  1010.  
  1011.             *theData = myDataRec->fOrigPtr;
  1012.         }
  1013.     }
  1014.     
  1015.     return(myErr);
  1016. }
  1017.